package cn.mdmbct.douyin.basis.service.executor;

import cn.mdmbct.douyin.basis.body.video.*;
import cn.mdmbct.douyin.basis.response.video.ImageUploadRes;
import cn.mdmbct.douyin.basis.response.video.VideoCreateRes;
import cn.mdmbct.douyin.basis.response.video.VideoUploadRes;
import cn.mdmbct.douyin.basis.service.interfaces.VideoManagerService;
import cn.mdmbct.douyin.common.enums.ApiPlatform;
import cn.mdmbct.douyin.common.enums.RequestBodyType;
import cn.mdmbct.douyin.common.exception.ApiCallException;
import cn.mdmbct.douyin.common.exception.InvalidRequestParamException;
import cn.mdmbct.douyin.common.executor.ServiceExecutor;
import cn.mdmbct.douyin.common.http.ByteRequestBody;
import cn.mdmbct.douyin.common.http.FileRequestBody;
import cn.mdmbct.douyin.common.http.Head;
import cn.mdmbct.douyin.common.proxy.ServiceExecutorProvider;
import cn.mdmbct.douyin.common.proxy.annotations.Mapping;
import cn.mdmbct.douyin.common.response.DefaultResponse;
import cn.mdmbct.douyin.common.response.DefaultResponseData;
import cn.mdmbct.douyin.common.session.DySession;
import cn.mdmbct.douyin.common.utils.ApiResultHandler;
import cn.mdmbct.douyin.common.utils.file.FileChunkHandler;
import cn.mdmbct.douyin.common.utils.file.FileSplit;
import cn.mdmbct.douyin.common.utils.file.FileUtils;
import cn.mdmbct.douyin.common.utils.file.MultiThreadFileChunkGroupHandler;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Collections;

/**
 * @author mdmbct  mdmbct@outlook.com
 * @date 5/8/2021 21:53 PM
 * @modified mdmbct
 * @since 1.0
 */
@Slf4j
public class VideoManagerServiceProvider extends ServiceExecutorProvider<VideoManagerService> {


    public VideoManagerServiceProvider(ServiceExecutor serviceExecutor, VideoManagerService service) {
        super(serviceExecutor, service);
    }

    /**
     * 视频上传
     *
     * @see VideoManagerService#videoUpload(String, File, ApiPlatform)
     */
    public VideoUploadRes.VideoUploadResponseData videoUpload(Mapping mapping, String openId, File videoFile, ApiPlatform platform) throws ApiCallException {

        if (videoFile.length() > VideoManagerService.MAX_SINGLE_VIDEO_FILE_SIZE) {
            throw new InvalidRequestParamException("大于128MB的视频,请使用分片上传!");
        }

        if (!videoFile.exists()) {
            throw new InvalidRequestParamException("视频文件不存在!");
        }

        String url = serviceExecutor.formatRequestUrl(mapping, platform, openId);

        String fileName = videoFile.getName();
        String fileExtension = FileUtils.getFileExtension(fileName, false);
        FileRequestBody body = new FileRequestBody("video", fileName, "video/" + fileExtension, videoFile);

        String result = serviceExecutor.postRequest(url, body, openId, platform, RequestBodyType.FROM, Head.MULTIPART_FORM_DATA);
        return ApiResultHandler.handleApiResponseData(result, VideoUploadRes.class);
    }


    /**
     * 分片上传
     *
     * @see VideoManagerService#videoPartUpload(String, String, int, VideoUploadReqBody, File, ApiPlatform)
     */
    public DefaultResponseData videoPartUpload(Mapping mapping, String openId, String uploadId, int partNumber, VideoUploadReqBody partVideo, File videoFile, ApiPlatform platform) {

        byte[] part = partVideo.getVideo();

        if (part.length < VideoManagerService.MIN_PART_VIDEO_FILE_SIZE || part.length > VideoManagerService.DEFAULT_PART_VIDEO_FILE_SIZE) {
            throw new InvalidRequestParamException("一个视频分片的大小在5MB至20MB之间");
        }

        log.info("part_number:{}, part_length:{}, upload_id:{}, fileName:{}", partNumber, part.length, uploadId, videoFile.getAbsolutePath());

        String url = serviceExecutor.formatRequestUrl(mapping, platform, openId, uploadId, partNumber);
        ByteRequestBody body = new ByteRequestBody("video", videoFile.getName(), part);

        String result = serviceExecutor.postRequest(url, body, openId, platform, RequestBodyType.FROM, Head.MULTIPART_FORM_DATA);
        return ApiResultHandler.handleApiResponseData(result, DefaultResponse.class);

    }


    /**
     * 图片上传
     *
     * @see VideoManagerService#imageUpload(String, File)
     */
    public ImageUploadRes.ImageUploadResData imageUpload(Mapping mapping, String openId, File imageFile) throws ApiCallException {

        if (imageFile.length() >= VideoManagerService.MAX_ALLOW_IMAGE_SIZE) {
            throw new InvalidRequestParamException("抖音允许上传的图片大小不能超过100MB!");
        }

        if (!imageFile.exists()) {
            throw new InvalidRequestParamException("图片文件不存在!");
        }

        String url = serviceExecutor.formatRequestUrl(mapping, ApiPlatform.DOU_YIN, openId);

        String fileName = imageFile.getName();
        String extension = FileUtils.getFileExtension(fileName, false);
        FileRequestBody body = new FileRequestBody("image", fileName, "image/" + extension, imageFile);

        String result = serviceExecutor.postRequest(url, body, openId, ApiPlatform.DOU_YIN, RequestBodyType.FROM, Head.MULTIPART_FORM_DATA);
        return ApiResultHandler.handleApiResponseData(result, ImageUploadRes.class);
    }

    VideoCreateRes.VideoCreateResData autoCreateImage(String openId, File imageFile, AutoCreateImageParam autoCreateImageParam) throws ApiCallException {

        ImageUploadRes.ImageUploadResData imageUploadResData = service.imageUpload(openId, imageFile);

        ImageCreateBody body = ImageCreateBody.build(autoCreateImageParam);
        body.setImageId(imageUploadResData.getImage().getImageId());

        return service.imageCreate(openId, body);
    }

    VideoUploadRes.VideoUploadResponseData autoUploadVideo(String openId,
                                                                   File videoFile,
                                                                   ApiPlatform platform,
                                                                   DySession storageManager)
            throws ApiCallException, FileNotFoundException {

        long fileSize = videoFile.length();
        if (fileSize >= VideoManagerService.MAX_ALLOW_VIDEO_FILE_SIZE) {
            throw new InvalidRequestParamException("抖音允许上传的视频文件最大为4GB");
        }

        // 小于128MB 直接上传
        if (fileSize < VideoManagerService.MAX_SINGLE_VIDEO_FILE_SIZE) {
            return service.videoUpload(openId, videoFile, platform);
        }

        if (!videoFile.exists()) {
            throw new InvalidRequestParamException("视频文件不存在!");
        }

        // 分片上传
        String uploadId = service.videoPartInit(openId, platform).getUploadId();

        int threadCount = storageManager.getOpenApiConfig().getUploadThreadNum();
        FileSplit fileSplit = new FileSplit(videoFile, VideoManagerService.DEFAULT_PART_VIDEO_FILE_SIZE, VideoManagerService.MIN_PART_VIDEO_FILE_SIZE, threadCount);
        // 防止线程数超过分块数
        threadCount = Math.min(threadCount, fileSplit.getChunkCount());

        if (threadCount == 1) {
            // 单线程上传文件
            fileSplit.split(chunkGroup -> Collections.singletonList(
                    service.videoPartUpload(openId, uploadId, chunkGroup.get(0).getChunkNum(), new VideoUploadReqBody(chunkGroup.get(0).getBytes()), videoFile, platform)
            ));

        } else {
            // 多线程上传文件
            FileChunkHandler<DefaultResponseData> fileChunkHandler = chunk -> service.videoPartUpload(openId, uploadId, chunk.getChunkNum(), new VideoUploadReqBody(chunk.getBytes()), videoFile, platform);
            MultiThreadFileChunkGroupHandler<DefaultResponseData> multiThreadFileChunkHandler = new MultiThreadFileChunkGroupHandler<>(threadCount, fileSplit.getChunkCount(), fileChunkHandler);
            fileSplit.split(multiThreadFileChunkHandler);
            // 通知线程池中线程结束
            multiThreadFileChunkHandler.finishUpload();
        }

        fileSplit.finishedSplit();
        return service.videoPartComplete(openId, uploadId, platform);
    }

    VideoCreateRes.VideoCreateResData autoCreateVideo(String openId,
                                                      File videoFile,
                                                      AutoCreateVideoParam param,
                                                      ApiPlatform platform,
                                                      DySession storageManager)
            throws ApiCallException, FileNotFoundException {

        long fileSize = videoFile.length();
        if (fileSize >= VideoManagerService.MAX_ALLOW_VIDEO_FILE_SIZE) {
            throw new InvalidRequestParamException("抖音允许上传的视频文件最大为4GB");
        }

        VideoUploadRes.VideoUploadResponseData videoUploadResponseData = autoUploadVideo(openId, videoFile, platform, storageManager);
        String videoId = videoUploadResponseData.getVideo().getVideoId();

        VideoCreateReqBody body = VideoCreateReqBody.build(param);
        body.setVideoId(videoId);

        return service.videoCreate(openId, body, platform);
    }
}
